/**************************************************************************
*   Copyleft 2009 by        											  *
*	Iwanicki Krzysztof													  *
*   <www.gamma.mini.pw.edu.pl/~iwanickik>		                          *
*                                                                         *
*   This program is free software; you can redistribute it and/or modify  *
*   it under the terms of the GNU General Public License as published by  *
*   the Free Software Foundation; either version 2 of the License, or     *
*   (at your option) any later version.                                   *
*                                                                         *
*   This program is distributed in the hope that it will be useful,       *
*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
*   GNU General Public License for more details.                          *
***************************************************************************/
#include "./vMemoryTracker.h"

#ifdef V_DO_MEMORY_TRACKING
namespace Vendetta {


	// --------------------------------------------------
	//	Construct
	// --------------------------------------------------

	vMemoryTracker::vMemoryTracker():mAllocated(0), mFreed(0), mpAllocs(vNull), mpFreeAllocs(vNull), mNumAllocs(0)
	{}

	// --------------------------------------------------
	//	Destruct
	// --------------------------------------------------

	vMemoryTracker::~vMemoryTracker() {
		dumpInfo();
	}

	// --------------------------------------------------
	//	alloc
	// --------------------------------------------------

	vVoid* vMemoryTracker::Alloc(const vUInt size, const vChar* file, const vInt line) {
		vVoid* ptr = ::malloc(size);
		add(vUInt(ptr), size, file, line);

		return ptr;
	}

	// --------------------------------------------------
	//	free
	// --------------------------------------------------

	vVoid vMemoryTracker::Free(vVoid* ptr) {
		if (!remove(vUInt(ptr))) {
			std::cout<< "<!>Couldn't free memory at " << vUInt(ptr) << "\n";
			return;
		}
		::free(ptr);
	}

	vVoid vMemoryTracker::dumpInfo() {
		std::ofstream output;
		output.open(V_MEMORY_LEAKS_FILE);
		output<<"+-----------------------------------------------------------------------------+"<<std::endl;
		output<<"|          Vendetta memory tracer system made by Iwanicki Krzysztof           |"<<std::endl;
		output<<"|                  <http://gamma.mini.pw.edu.pl/~iwanickik>                   |"<<std::endl;
		output<<"+-----------------------------------------------------------------------------+"<<std::endl;
		output<<"---------------------------------- DUMP INFO ----------------------------------"<<std::endl;;
		output<<"Allocated memory: "<<mAllocated/1024<<" KB"<<std::endl;
		output<<"Freed memory: "<<mFreed/1024<<" KB"<<std::endl<<std::endl;
		tAlloc* tmp = mpAllocs;
		if (tmp) {
			output<<"Memory leaks:"<<std::endl;
		}
		tAlloc* prev = vNull;
		while (tmp) {
			output<<"  "<<"Address="<<tmp->Address<<", File="<<tmp->File<<", Line="<<tmp->Line<<", Size="<<tmp->Size<<" B"<<std::endl;
			tmp	= tmp->Next;
		}
		output.close();
	}

	// --------------------------------------------------
	//	add
	// --------------------------------------------------

	vVoid vMemoryTracker::add(const vUInt address, const vUInt size, const vChar* file, const vInt line) {
		mAllocated += size;

		// See if we need to malloc some new memory
		tAlloc* tmp;
		if (mpFreeAllocs) {
			tmp	= mpFreeAllocs;
			mpFreeAllocs = mpFreeAllocs->Next;
		} else {
			tmp	= (tAlloc*) ::malloc(sizeof(tAlloc));
		}

		tmp->Address = address;
		if (file) {
			
			// Copy actual file name only (discard whole path)
			vInt startPos	= 0;
			vInt pos		= 0;
			while (file[pos] != '\0') {
				if ((file[pos] == '\\') ||	(file[pos] == '/'))	{ // In Debug mode... || ...in Release mode....why???					
					startPos = pos + 1;
				}
				pos++;
			}
			::memcpy(tmp->File, file + startPos, pos - startPos + 1);
		} else {
			::memcpy(tmp->File, "?", 2);
		}

		tmp->Line	= line;
		tmp->Size	= size;
		tmp->Next	= mpAllocs;
		
		mpAllocs	= tmp;
		mNumAllocs++;
	}

	// --------------------------------------------------
	//	remove
	// --------------------------------------------------
	vBool vMemoryTracker::remove(const vUInt address) {
		tAlloc* tmp = mpAllocs;
		tAlloc* prev = vNull;
		while (tmp) {
			if (tmp->Address == address) {
				mFreed += tmp->Size;
				if (prev) {
					prev->Next = tmp->Next;
				} else {
					mpAllocs = tmp->Next;
				}
				mNumAllocs--;

				// Move it to list of free elements
				tmp->Next = mpFreeAllocs;
				mpFreeAllocs = tmp;
				return true;
			}
			prev = tmp;
			tmp	= tmp->Next;
		}
		return false;
	}
}
#endif